#ifndef DISPENSEHEAD_H
#define DISPENSEHEAD_H

#include "AxisModule/singleaxis.h"
#include "AxisModule/xymodule.h"
#include "BasicElement/basicelementfactory.h"
#include "CompositeElement/sccylinder.h"
#include "Laser/htg1laser.h"
#include "Tray/sensortraymap.h"
#include "Tray/sensortraymaterialdata.h"
#include "Tray/trayconfig.h"
#include "VisionLocation/visionlocation.h"
#include "VisionManager/visionmanager.h"
#include "dispenseheadconfig.h"
#include "namedefinition.h"
#include "systemCore/worker.h"
#include <qmath.h>

class DispenseWorkHolder;

struct PrProcessingData
{
public:
    PrProcessingData() {}
    PrProcessingData(QImage &image, SensorTrayMaterialData *materialData) : image(image), materialData(materialData) {}

    QImage image;
    SensorTrayMaterialData *materialData;
};

struct GlueCheckData
{
public:
    GlueCheckData() {}
    GlueCheckData(QImage &imageBefore, QImage &imageAfter, SensorTrayMaterialData *materialData)
        : imageBefore(imageBefore), imageAfter(imageAfter), materialData(materialData)
    {
    }

    QImage imageBefore;
    QImage imageAfter;
    SensorTrayMaterialData *materialData;
};

class DispenseHead;

class DispenseImageProcessor : public QThread
{
    Q_OBJECT

public:
    enum ImageProcessType
    {
        None,
        Pr,
        GlueCheck
    };

    DispenseImageProcessor(DispenseHead *dispHead);

    void setImageProcessType(ImageProcessType type);
    void processPr(QImage &image, SensorTrayMaterialData *materialData);
    void processGlueCheck(QImage &imageBefor, QImage &imageAfter, SensorTrayMaterialData *materialData);

    // QThread interface
protected:
    void run();

private:
    QQueue<PrProcessingData> prProcessingDatas;
    QQueue<GlueCheckData> glueCheckDatas;
    QMutex locker;
    ImageProcessType imageProcessType = None;
    DispenseHead *dispHead;
};

class DispenseHead : public Worker
{
    Q_OBJECT
public:
    DispenseHead(bool isLeftDispHead,
                 DispenseHeadCommonConfig *dhCommonConfig,
                 HTG1Laser *laser,
                 TrayConfig *sensorTrayConfig,
                 SensorTrayMap *sensorTrayMap,
                 BasicElementFactory *motionElementFactory);

    void setAnotherDispHead(DispenseHead *value);

    DispenseHeadConfig *getDhConfig() const;

    // Worker interface
protected:
    virtual void initEventHandle() override;
    virtual void startEventHandle(const WorkerEvent &event) override;
    virtual void stopEventHandle() override;
    virtual void allWorkerStopped() override {}
    virtual void purgeEventHandle() override;
    virtual void resetEventHandle() override {}
    virtual void homeWorker(bool moveToReadyPosAfterHome) override;

    // 手动调试操作
public slots:
    void readDispensePath();

    void measureTrayLevel();

    void calibrateNozzelToCameraTipOffset();

    void calculateNozzelToCameraOffset();

    void calibrateLaserToCameraTipOffset();

    void calibrateNozzelToLaserTipOffset();

    void calibrateShotGlueHeight(int unitRowIndex, int unitColumnIndex, int rowIndex, int columnIndex);

    void calibrateShotGlueDotDelay();

    void shotGlueLine(double lineLen);

    void shotRectangle(double width, double height);

    double getCurrentRightDispXPos();

    void calculateShotGlueDotDelay(double x1, double x2);

    void moveToPrPos(int unitRowIndex, int unitColumnIndex, int rowIndex, int columnIndex);

    void saveImageForEditDispPath();

    QString getDispensePathResultImageName();

    void moveToMeasureHeight(int unitRowIndex, int unitColumnIndex, int rowIndex, int columnIndex);

    void moveToShotGlue(int unitRowIndex, int unitColumnIndex, int rowIndex, int columnIndex, bool doGlueCheck);

    void moveToShotGlueDot();

    void readRightDH2LeftDHSafetyRange();

    void performAllSensorShotGlue();

    double glueUsedTime() const;

    void newGlueSetUp();

    // 自动运行逻辑
public slots:
    void performAllSensorPr();
    void shotGlue(int rowIndex, int columnIndex);
    void gotNewTray();
    void distributeRemainedShotGlueTask();
    void performAllSensorGlueCheck();
    void doDummyGlueCheck();
    void notifyHasDoneWork();
    void upDispZ();
    void moveToReadyPos();
    void waitWholeTrayDoneShotGlueTask();
    void waitLeftDispZArrivedStandbyPos();
    void moveToStandbyPos();
    void moveXyzOrZToStandbyPos();
    void tryMoveToPrLeftTopPos();

private:
    QString dispensePathDir() const
    {
        return QString("%1/dispensePath/").arg(getWorkerConfigDir());
    }

    void nozzelToLaserTipOffsetFinally(QList<double> &heights, QList<double> &axisPos, QList<double> &deltaHeights);

    double getAxisPosWhileLaserHeightChanged(SCAxis *axis, double targetPos, QString type);

    void getAxisPosWhileLaserHeightChangedFinally(SCAxis *axis,
                                                  QList<double> &axisPoses,
                                                  QList<double> &rawHeights,
                                                  QList<double> &filterHeights,
                                                  QList<double> &deltaHeights,
                                                  QString &type);

    QVector<PathEndPoint> mapDispensePath(SensorTrayMaterialData *sensorTrayMaterialData, double z);

    QVector<PathEndPoint> mapDispensePath(PrOffset prOffset, QPointF prXyPos, double z);

    QPointF mapMeasureHeightPoint(SensorTrayMaterialData *sensorTrayMaterialData);

    QPointF mapMeasureHeightPoint(PrOffset prOffset, QPointF prXyPos);

    void tryToDistributeShotGlueTaskToLeftDH();

    SensorTrayMaterialData *lastColumnDataHandledByLeftDH(int currentColumn);

    SensorTrayMaterialData *lastColumnDataHandledByRightDH(int currentColumn, bool isLeftToRight);

    bool needMeasureHeight(bool checkReallyMeasuredHeight);

    void waitIsAbleToExecuteTask(SensorTrayMaterialData *sensorTrayMaterialData);

    bool leftDHIsAbleToExecuteTask(SensorTrayMaterialData *sensorTrayMaterialData);

    bool rightDHIsAbleToExecuteTask(SensorTrayMaterialData *sensorTrayMaterialData);

    double getLeftDHExecuteTaskMaxPos(SensorTrayMaterialData *sensorTrayMaterialData);

    double getRightDHExecuteTaskMinPos(SensorTrayMaterialData *sensorTrayMaterialData);

    void distributeAllShotGlueTaskToRightDH();

    void distributeAllShotGlueTaskToLeftDH();

    ///
    /// \brief applyLeftDHFirstDistributionStrategy
    /// 左点胶头优先策略
    ///
    void applyLeftDHFirstDistributionStrategy(int startColumn, int endColumn);

    void moveToPrPosImpl(int unitRowIndex, int unitColumnIndex, int rowIndex, int columnIndex);

    PrOffset moveToPerformPrImpl(int unitRowIndex, int unitColumnIndex, int rowIndex, int columnIndex);

    PrOffset moveToMeasureHeightImpl(int unitRowIndex,
                                     int unitColumnIndex,
                                     int rowIndex,
                                     int columnIndex,
                                     double &laserHeight,
                                     double &shotGlueHeight);

    void executeDispensePath(QVector<PathEndPoint> &dispensePath);

    QPointF getPrXyPos(int rowIndex, int columnIndex);

    QPointF getPrXyPos(int unitRowIndex, int unitColumnIndex, int rowIndex, int columnIndex);

    DispenseHead *leftDH()
    {
        return isLeftDispHead ? this : anotherDispHead;
    }

    DispenseHead *rightDH()
    {
        return isLeftDispHead ? anotherDispHead : this;
    }

    bool wholeTrayHasDoneShotGlueTask();

    QString index2String(int rowIndex, int columnIndex);
    QPoint string2Index(QString s);
    bool glueCheck(QImage &imageBefore, QImage &imageAfter);

private:
    friend class DispenseWorkHolder;
    friend class DispenseImageProcessor;

    bool isLeftDispHead;

    DispenseHeadConfig *dhConfig;
    ConfigFile *dhConfigFile;
    DispenseHeadCommonConfig *dhCommonConfig;
    SCDispenser *dispenser;

    DispenseImageProcessor *dispImageProcessor = nullptr;
    QMap<QString, QImage> imagesBeforeDispense;

    DispenseHead *anotherDispHead;

    SingleAxis *dispZ;
    XYZModule *dispXYZ;
    SCCylinder *dispZLockCyl;
    SCDO *shotGlueOut;
    HTG1Laser *laser;
    TrayConfig *sensorTrayConfig;
    SensorTrayMap *sensorTrayMap;
    Calibration *trayRawSensorCalibration;
    VisionLocation *trayRawSensorLocation;
    VisionLocation *laserToCameraOffsetCaliLocation;
    VisionLocation *glueDotLocation;
    QVector<QPointF> mechDispPath;

    bool hasMeasureHeightForThisTray = false;
    bool gotShotGlueTaskForThisTray = false;
    double shotGlueZHeight = 0;

    bool isWaitForExecuteShotGlueTask = false;

    bool disableLeftDh;
    bool disableRightDh;
    bool executeGlueCheck;
};

#endif    // DISPENSEHEAD_H
